<!DOCTYPE html>
<!--SPWGIF (Signal Processing Web Graphic Interface Framework)
    Copyright (C) 2015  Dimitar Nikolov

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
	
	You could contact me by email at dnikolov.co.nf@gmail.com
	For a copy of the GPL v3 check also at dnikolov.co.nf/html/gplv3.html
-->
<html>
	<head>
		<meta charset="UTF-8" />
		<meta name="description" content="Dimitar Nikolov's personal webpage. You can connect with me, find what I'm up to or take a look at  my articles or projects." />
		<meta name="keywords" content="Dimitar Nikolov,Dimitar,Nikolov,Contact,Personal page,Personal site,Blog,Software Projects,Articles,Compositions" />
		<meta name="author" content="Dimitar Nikolov" />
		
		<link rel="stylesheet" href="/css/reset.css" />
		<link rel="stylesheet" href="/css/main.css" />
		
		<script src="/js/SignalProcessingWebGraphicInterfaceFramework.js"></script>
		<script>
			function init(){
				var currentInterfaceState = {
					slider1:{},
					slider2:{},
					slider3:{},
					knob:{},
					grayScaleButton:{},
					noiseSlider:{},
					binarizationButton:{},
					currentApplyImage:{},
					currentDisplayMonitor:{},
					currentDisplayImage:{}
				};
				var skipEvents = false;
				var interfaceStates = {};
				
				var spif = new SPWGIF();
				var panel = spif.componentManager.createComponent({
					type:"panel",
					containerID:"main",
					drawParams:{
						width:"100%",
						height:"800px"
					}
				});
				var monitor = spif.componentManager.createComponent({
					type:'monitor',
					containerID:panel.id,
					drawParams: {
						width:"400px",
						height:"300px",
						left:"20px",
						top:"20px"
					}
				});
				var monitor1Label = spif.componentManager.createComponent({
					type:'label',
					containerID:panel.id,
					drawParams:{
						content:"Original image (monitor 1)",
						width:"400px",
						height:"20px",
						left:"20px",
						top:"325px"
					}
				});
				var firstResultMonitor = spif.componentManager.createComponent({
					type:'monitor',
					containerID:panel.id,
					drawParams: {
						width:"400px",
						height:"300px",
						left:"430px",
						top:"20px"
					}
				});
				var monitor2Label = spif.componentManager.createComponent({
					type:'label',
					containerID:panel.id,
					drawParams:{
						content:"First result (monitor 2)",
						width:"400px",
						height:"20px",
						left:"430px",
						top:"325px"
					}
				});
				var secondResultMonitor = spif.componentManager.createComponent({
					type:'monitor',
					containerID:panel.id,
					drawParams: {
						width:"400px",
						height:"300px",
						left:"840px",
						top:"20px"
					}
				});
				var monitor3Label = spif.componentManager.createComponent({
					type:'label',
					containerID:panel.id,
					drawParams:{
						content:"Second result (monitor 3)",
						width:"400px",
						height:"20px",
						left:"840px",
						top:"325px"
					}
				});
				var slider1 = spif.componentManager.createComponent({
					type:'slider',
					containerID:panel.id,
					drawParams:{
						width:"90px",
						height:"240px",
						divisions:11,
						divisionStep:0.1,
						left:"390px",
						top:"380px",
						display:true,
						label:"Red"
					}
				}).controlInterface.slideTo(1);
				var slider2 = spif.componentManager.createComponent({
					type:'slider',
					containerID:panel.id,
					drawParams:{
						width:"90px",
						height:"240px",
						divisions:11,
						divisionStep:0.1,
						left:"490px",
						top:"380px",
						display:true,
						border:"1px solid black",
						label:"Green"
					}
				}).controlInterface.slideTo(1);	
				var slider3 = spif.componentManager.createComponent({
					type:'slider',
					containerID:panel.id,
					drawParams:{
						width:"90px",
						height:"240px",
						divisions:11,
						divisionStep:0.1,
						left:"590px",
						top:"380px",
						display:true,
						border:"1px solid black",
						label:"Blue"
					}
				}).controlInterface.slideTo(1);
				var label1 = spif.componentManager.createComponent({
					type:'label',
					containerID:panel.id,
					drawParams: {
						content:"Multipliers of the color components",
						width:"290px",
						height:"20px",
						left:"390px",
						top:"630px"
					}
				});
				var grayScaleButton = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams: {
						label:"Gray scale",
						width:"100px",
						height:"30px",
						left:"730px",
						top:"380px"
					}
				});
				var binarizationButton = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams:{
						label:"Binarization",
						width:"100px",
						height:"30px",
						left:"850px",
						top:"380px"
					}
				});
				var knob = spif.componentManager.createComponent({
					type:"knob",
					containerID:panel.id,
					drawParams:{
						top:"450px",
						width:"200px",
						height:"200px",
						divisions:16,
						divisionStep:16,
						display:true,
						label:"Binarization treshold",
						decimalPlaces:0,
						left:"740px"
					},
					controlInterfaceParams:{
						maxTurnPosition:255
					}
				}).controlInterface.turnTo(127);
				var noiseAndFiltrationLabel = spif.componentManager.createComponent({
					type:'label',
					containerID:panel.id,
					drawParams: {
						content:"Noise and filtration",
						width:"210px",
						height:"20px",
						left:"1000px",
						top:"380px"
					}
				});
				var noiseSlider = spif.componentManager.createComponent({
					type:'slider',
					containerID:panel.id,
					drawParams:{
						width:"80px",
						height:"240px",
						divisions:16,
						divisionStep:1,
						left:"1000px",
						top:"410px",
						display:true,
						label:"Noise [%]",
						decimalPlaces:0
					}
				}).controlInterface.slideTo(0);
				var classicFilter = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams:{
						width:"120px",
						height:"40px",
						divisions:11,
						divisionStep:1,
						left:"1090px",
						top:"410px",
						display:true,
						label:"Classic 3x3 filter",
						decimalPlaces:0
					}
				});
				var gaussianFilter3x3 = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams:{
						width:"120px",
						height:"40px",
						divisions:11,
						divisionStep:1,
						left:"1090px",
						top:"460px",
						display:true,
						label:"Gausian 3x3 filter",
						decimalPlaces:0
					}
				});
				var gaussianFilter5x5 = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams:{
						width:"120px",
						height:"40px",
						divisions:11,
						divisionStep:1,
						left:"1090px",
						top:"510px",
						display:true,
						label:"Gausian 5x5 filter",
						decimalPlaces:0
					}
				});
				var openButton = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams:{
						label:"Open image",
						width:"100px",
						height:"30px",
						left:"20px",
						top:"360px",
						subType:"openFile"
					}
				});
				var applyToLabel = spif.componentManager.createComponent({
					type:'label',
					containerID:panel.id,
					drawParams: {
						content:"Apply to:",
						width:"320px",
						height:"20px",
						left:"20px",
						top:"410px"
					}
				});
				var applyToMonitor1Button = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams: {
						label: "Monitor 1",
						width: "100px",
						height: "30px",
						left: "20px",
						top: "440px"
					}
				});
				var applyToMonitor2Button = spif.componentManager.createComponent({
					type:'button',
					containerID: panel.id,
					drawParams: {
						label: "Monitor 2",
						width: "100px",
						height: "30px",
						left: "130px",
						top: "440px"
					}
				});
				var applyToMonitor3Button = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams:{
						label:"Monitor 3",
						width:"100px",
						height:"30px",
						left:"240px",
						top:"440px"
					}
				});
				var displayInLabel = spif.componentManager.createComponent({
					type:'label',
					containerID:panel.id,
					drawParams: {
						content:"Display in:",
						width:"320px",
						height:"20px",
						left:"20px",
						top:"495px"
					}
				});
				var displayInMonitor2Button = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams: {
						label: "Monitor 2",
						width: "100px",
						height: "30px",
						left: "70px",
						top: "525px"
					}
				});
				var displayInMonitor3Button = spif.componentManager.createComponent({
					type:'button',
					containerID: panel.id,
					drawParams: {
						label: "Monitor 3",
						width: "100px",
						height: "30px",
						left: "180px",
						top: "525px"
					}
				});
				var calculateDifferenceLabel = spif.componentManager.createComponent({
					type:'label',
					containerID:panel.id,
					drawParams: {
						content:"Detect difference between monitor 3 and monitor 2 and put it in monitor 3:",
						width:"320px",
						height:"40px",
						left:"20px",
						top:"580px"
					}
				});
				var calculateDifferenceButton = spif.componentManager.createComponent({
					type:'button',
					containerID:panel.id,
					drawParams: {
						label:"Detect",
						width:"100px",
						height:"30px",
						left:"120px",
						top:"630px"
					}
				});
				
				var currentApplyMonitor;
				var currentDisplayMonitor;
				var currentApplyImage;
				var currentOriginalImage;				
				var lastAddedImageID;
				
				openButton.controlInterfaceParams.onChange = function(chosenFile){
					if(lastAddedImageID || lastAddedImageID === 0){
						monitor.controlInterface.removeImage(lastAddedImageID);
					}
					firstResultMonitor.controlInterface.clearMonitor();
					secondResultMonitor.controlInterface.clearMonitor();
					slider1.controlInterface.slideTo(1);
					slider2.controlInterface.slideTo(1);
					slider3.controlInterface.slideTo(1);
					monitor.controlInterfaceParams.onLoad = function(image){
						currentInterfaceState.currentApplyImage.imageWrapper = monitor.controlInterface.getImageCopy('on top');
						currentOriginalImage = monitor.controlInterface.getImageCopy('on top');
					}
					lastAddedImageID = monitor.controlInterface.addImage(chosenFile);
				}
				applyToMonitor1Button.controlInterfaceParams.onPress = function(state){
					if(state==='on'){
						calculateDifferenceButton.controlInterface.off();
						applyToMonitor2Button.controlInterface.off();
						applyToMonitor3Button.controlInterface.off();
						slider1.controlInterface.slideTo(1);
						slider2.controlInterface.slideTo(1);
						slider3.controlInterface.slideTo(1);
						currentApplyMonitor = monitor;
						currentInterfaceState.currentApplyImage.imageWrapper = currentApplyMonitor.controlInterface.getImageCopy('on top');
						currentOriginalImage = currentApplyMonitor.controlInterface.getImageCopy('on top');
					}
				}
				applyToMonitor1Button.controlInterface.on();
				applyToMonitor2Button.controlInterfaceParams.onPress = function(state){
					if(state==='on'){
						calculateDifferenceButton.controlInterface.off();
						applyToMonitor1Button.controlInterface.off();
						applyToMonitor3Button.controlInterface.off();
						slider1.controlInterface.slideTo(1);
						slider2.controlInterface.slideTo(1);
						slider3.controlInterface.slideTo(1);						
						currentApplyMonitor = firstResultMonitor;
						currentInterfaceState.currentApplyImage.imageWrapper = currentApplyMonitor.controlInterface.getImageCopy('on top');
						currentOriginalImage = currentApplyMonitor.controlInterface.getImageCopy('on top');
					}
				}
				applyToMonitor3Button.controlInterfaceParams.onPress = function(state){
					if(state==='on'){
						calculateDifferenceButton.controlInterface.off();
						applyToMonitor1Button.controlInterface.off();
						applyToMonitor2Button.controlInterface.off();
						slider1.controlInterface.slideTo(1);
						slider2.controlInterface.slideTo(1);
						slider3.controlInterface.slideTo(1);	
						currentApplyMonitor = secondResultMonitor;
						currentInterfaceState.currentApplyImage.imageWrapper = currentApplyMonitor.controlInterface.getImageCopy('on top');
						currentOriginalImage = currentApplyMonitor.controlInterface.getImageCopy('on top');
					}
				}
				displayInMonitor2Button.controlInterfaceParams.onPress = function(state){
					if(state === 'on'){
						calculateDifferenceButton.controlInterface.off();
						displayInMonitor3Button.controlInterface.off();
						currentInterfaceState.currentDisplayMonitor.monitor = firstResultMonitor;
					}
				}
				displayInMonitor2Button.controlInterface.on();
				displayInMonitor3Button.controlInterfaceParams.onPress = function(state){
					if(state==='on'){
						calculateDifferenceButton.controlInterface.off();
						displayInMonitor2Button.controlInterface.off();
						currentInterfaceState.currentDisplayMonitor.monitor = secondResultMonitor;
					}
				}
				slider1.controlInterfaceParams.onChange = function(){
					if(skipEvents)return;
					currentInterfaceState.slider1.value = slider1.controlInterface.getValue();
					if(currentInterfaceState.currentApplyImage.imageWrapper){
						var pixels = new Uint8ClampedArray(currentInterfaceState.currentApplyImage.imageWrapper.imageData.data);
						var originalPixels = new Uint8ClampedArray(currentOriginalImage.imageData.data);
						for(var k=0;k<pixels.length;k+=4){
							pixels[k] = originalPixels[k] * currentInterfaceState.slider1.value;
						}
						if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(currentOriginalImage);
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
								currentOriginalImage = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top',pixels);
								currentInterfaceState.currentApplyImage.imageWrapper = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
							}
						}else{
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top',pixels);
							currentInterfaceState.currentApplyImage.imageWrapper = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
						}
					}else{
						console.info('slider could not find apply image');
					}
				}
				slider2.controlInterfaceParams.onChange = function(){
					if(skipEvents)return;
					currentInterfaceState.slider2.value = slider2.controlInterface.getValue();
					if(currentInterfaceState.currentApplyImage.imageWrapper){
						var pixels = new Uint8ClampedArray(currentInterfaceState.currentApplyImage.imageWrapper.imageData.data);
						var originalPixels = new Uint8ClampedArray(currentOriginalImage.imageData.data);
						for(var k=1;k<pixels.length;k+=4){
							pixels[k] = originalPixels[k] * currentInterfaceState.slider2.value;
						}
						if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(currentOriginalImage);
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
								currentOriginalImage = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top',pixels);
								currentInterfaceState.currentApplyImage.imageWrapper = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
							}
						}else{
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top',pixels);
							currentInterfaceState.currentApplyImage.imageWrapper = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
						}
					}else{
						console.info('slider 2 could not find apply image');
					}
				}
				slider3.controlInterfaceParams.onChange = function(){
					if(skipEvents)return;
					currentInterfaceState.slider3.value = slider3.controlInterface.getValue();
					if(currentInterfaceState.currentApplyImage.imageWrapper){
						var pixels = new Uint8ClampedArray(currentInterfaceState.currentApplyImage.imageWrapper.imageData.data);
						var originalPixels = new Uint8ClampedArray(currentOriginalImage.imageData.data);
						for(var k=2;k<pixels.length;k+=4){
							pixels[k] = originalPixels[k] * currentInterfaceState.slider3.value;
						}
						if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(currentOriginalImage);
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
								currentOriginalImage = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top',pixels);
								currentInterfaceState.currentApplyImage.imageWrapper = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
							}
						}else{
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top',pixels);
							currentInterfaceState.currentApplyImage.imageWrapper = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
						}
					}else{
						console.info('slider 3 could not find apply image');
					}
				}
				var toSaveForAfterGrayScale = undefined;
				var originalPixelsBeforeGrayScale;
				var imageToSaveBeforeBinarisation;
				var makeBinary = false;
				var displayedGrayScaleImage;
				grayScaleButton.controlInterfaceParams.onPress = function(state){
					if(skipEvents)return;
					currentInterfaceState.grayScaleButton.state = state;
					if(state === "off"){
						slider1.controlInterface.slideTo(
							parseInt(currentInterfaceState.slider1.value)
						);
						slider2.controlInterface.slideTo(
							parseInt(currentInterfaceState.slider2.value)
						);
						slider3.controlInterface.slideTo(
							parseInt(currentInterfaceState.slider3.value)
						);
						slider1.controlInterface.on();
						slider2.controlInterface.on();
						slider3.controlInterface.on();
						if(binarizationButton.controlInterface.getState() === "on"){
							binarizationButton.controlInterface.off();
						}
						currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', new Uint8ClampedArray(toSaveForAfterGrayScale.imageData.data) );
						displayedGrayScaleImage = undefined;
					}else if( state === 'on' ){
						slider1.controlInterface.off();
						slider2.controlInterface.off();
						slider3.controlInterface.off();
						if( currentInterfaceState.currentApplyImage.imageWrapper ){
							toSaveForAfterGrayScale = currentApplyMonitor.controlInterface.getImageCopy('on top');
							originalPixelsBeforeGrayScale = new Uint8ClampedArray(toSaveForAfterGrayScale.imageData.data);
							var pixels = new Uint8ClampedArray(currentInterfaceState.currentApplyImage.imageWrapper.imageData.data);
							for(i=0;i<pixels.length;i+=4){
								var value = (pixels[i]*0.3+pixels[i+1]*0.59+pixels[i+2]*0.11);//Y = 0.11B + 0.59G + 0.3R
								value = value.toFixed(0);
								pixels[i]=value;
								pixels[i+1]=value;
								pixels[i+2]=value;
							}
							var calculateOtsusTreshold = function(eps){
								var imageHistogram = [];
								var tresholds = [];
								var iterationIndex = 0;
								var minImageValue;
								var maxImageValue;
								var initialThreshold;
								var eps = eps || 1;
								for(var i=0; i<displayedGrayScaleImage.imageData.data.length; i+=4){
									imageHistogram[displayedGrayScaleImage.imageData.data[i]] ?
										imageHistogram[displayedGrayScaleImage.imageData.data[i]] += 1 :
										imageHistogram[displayedGrayScaleImage.imageData.data[i]] = 1;
								}
								for(var i=0;i<imageHistogram.length;i++){
									if(imageHistogram[i]){
										if(!minImageValue){
											minImageValue = i;
										}
										maxImageValue = i;
									}
								}
								initialThreshold = parseInt( (minImageValue+maxImageValue)/2 );
								tresholds[iterationIndex] = initialThreshold;
								var numberOfIts = 0;
								do{
									numberOfIts++;
									var sumOfBackgroudHistogramElements = 0;
									var sumOfForegroundHistogramElements = 0;
									var nominator = 0;
									var denominator = 0;
									for(var i=0;i<tresholds[iterationIndex];i++){
										nominator += (i*imageHistogram[i]||0);
										denominator += (imageHistogram[i]||0);
									}
									nominator /= (displayedGrayScaleImage.width*displayedGrayScaleImage.height);
									denominator /= (displayedGrayScaleImage.width*displayedGrayScaleImage.height);
									sumOfBackgroudHistogramElements = parseInt(nominator/denominator);
									nominator = 0;
									denominator = 0;
									for(var j=tresholds[iterationIndex]+1;j<imageHistogram.length;j++){
										nominator += (j*imageHistogram[j]||0);
										denominator += (imageHistogram[j]||0);
									}
									nominator /= (displayedGrayScaleImage.width*displayedGrayScaleImage.height);
									denominator /= (displayedGrayScaleImage.width*displayedGrayScaleImage.height);
									sumOfForegroundHistogramElements = parseInt(nominator/denominator);
									if(isNaN((sumOfForegroundHistogramElements+sumOfBackgroudHistogramElements)/2))break;
									iterationIndex++;
									tresholds[iterationIndex] = parseInt((sumOfForegroundHistogramElements+sumOfBackgroudHistogramElements)/2);
								}
								while(Math.abs( tresholds[iterationIndex] - tresholds[iterationIndex - 1]) > eps);
								return tresholds[iterationIndex];
							}
							if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
									currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', pixels);
									displayedGrayScaleImage = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
									if(displayedGrayScaleImage){
										knob.controlInterface.turnTo(calculateOtsusTreshold());
									}else{
										console.info('No gray scale image displayed');
									}
									if(makeBinary){
										imageToSaveBeforeBinarisation = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
										///*var*/ pixels = new Uint8ClampedArray(currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top').imageData.data);
										pixels = new Uint8ClampedArray(imageToSaveBeforeBinarisation.imageData.data);
										var barrier = knob.controlInterface.getValue();
										for(i=0;i<pixels.length;i+=4){
											var value = pixels[i];
											if(isNaN(value)||value>255||value<0){
												console.info('Problem when determing gray scale pixel value');
												console.info(value);
											}
											if(isNaN(barrier)||barrier>255||barrier<0){
												console.info('Problem with barrier: '+barrier);
											}
											if(value > barrier){
												pixels[i] = 255;
												pixels[i+1] = 255;
												pixels[i+2] = 255;
											}else if(value <= barrier){
												pixels[i] = 0;
												pixels[i+1] = 0;
												pixels[i+2] = 0;
											}
										}
										currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', pixels);
									}
								}
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper( currentApplyMonitor.controlInterface.getImageCopy('on top') );
							}else{
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', pixels);
								displayedGrayScaleImage = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
								if( makeBinary ){
									imageToSaveBeforeBinarisation = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
									pixels = new Uint8ClampedArray( imageToSaveBeforeBinarisation.imageData.data );
									var barrier = knob.controlInterface.getValue();
									for(i=0;i<pixels.length;i+=4){
										var value = pixels[i];
										if(isNaN(value)||value>255||value<0){
											console.info('Problem when determing gray scale pixel value');
											console.info(value);
										}
										if(isNaN(barrier)||barrier>255||barrier<0){
											console.info('Problem with barrier: '+barrier);
										}
										if(value > barrier){
											pixels[i] = 255;
											pixels[i+1] = 255;
											pixels[i+2] = 255;
										}else if(value <= barrier){
											pixels[i] = 0;
											pixels[i+1] = 0;
											pixels[i+2] = 0;
										}
									}
									currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', pixels);
								}
								if( displayedGrayScaleImage ){
									if(!imageToSaveBeforeBinarisation){
										knob.controlInterface.turnTo(calculateOtsusTreshold());
									}
								}else{
									console.info('No gray scale image displayed');
								}
							}
						}else{
							console.info('No current apply image');
						}
					}
				};
				binarizationButton.controlInterfaceParams.onPress = function(state){
					if( skipEvents )return;
					currentInterfaceState.binarizationButton.state = state;
					if( state === "on" ){
						makeBinary = true;
						grayScaleButton.controlInterface.on();
						if(noiseSlider.controlInterface.getState()==="on"){
							noiseSlider.controlInterface.off();
						}
					}else if( state === 'off' ){
						makeBinary = false;
						if( imageToSaveBeforeBinarisation ){
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', new Uint8ClampedArray(imageToSaveBeforeBinarisation.imageData.data) );
							imageToSaveBeforeBinarisation = undefined;
							if(noiseSlider.controlInterface.getState()==='off')noiseSlider.controlInterface.on();
						}else{
							console.info('no image has been saved before binarisation');
						}
					}
				};
				var binarizationIterationReady = true;
				knob.controlInterfaceParams.onTurn = function(value){
					if(skipEvents)return;
					if(makeBinary){
						binarizationButton.controlInterface.on();
					}
				}
				var noiseIterationReady = true;
				noiseSlider.controlInterfaceParams.onChange = function(value){
					if(!noiseIterationReady||skipEvents||makeBinary){
						console.info('noise iteration not ready');
						return;
					};
					noiseIterationReady = false;
					var maxIterationIndex = value*(currentInterfaceState.currentApplyImage.imageWrapper.width*currentInterfaceState.currentApplyImage.imageWrapper.height)/100;
					maxIterationIndex = parseInt(maxIterationIndex.toFixed(0));
					var noisedPixels;
					var noisedPixelsIndexArray = [];
					for(var i=0;i<currentInterfaceState.currentApplyImage.imageWrapper.width;i++){
						noisedPixelsIndexArray[i] = new Array(currentInterfaceState.currentApplyImage.imageWrapper.height);
					}
					if( displayedGrayScaleImage ){
						console.info('image to noise is gray');
						noisedPixels = new Uint8ClampedArray( displayedGrayScaleImage.imageData.data );
						for(var i=0;i<maxIterationIndex;i++){
							var isPixelNoised = false;
							//Using Math.floor(Math.random() * (max - min +1)) + min for obtaining EVEN distribution
							//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
							//http://javascript.info/tutorial/number-math#tofixed-precision
							var emergencyEscape = 0;
							while(!isPixelNoised&&emergencyEscape < 100){
								emergencyEscape++;
								var xCoordinate = parseInt(Math.floor(Math.random()*(currentInterfaceState.currentApplyImage.imageWrapper.width)));
								var yCoordinate = parseInt(Math.floor(Math.random()*(currentInterfaceState.currentApplyImage.imageWrapper.height)));
								if(!noisedPixelsIndexArray[xCoordinate][yCoordinate] && noisedPixelsIndexArray[xCoordinate][yCoordinate]!==0 ){
									noisedPixelsIndexArray[xCoordinate][yCoordinate] = Math.floor(Math.random()*2) ? 0 : 255;
									noisedPixels[yCoordinate*toSaveForAfterGrayScale.width*4+xCoordinate*4] =
									noisedPixels[yCoordinate*toSaveForAfterGrayScale.width*4+xCoordinate*4+1] =
									noisedPixels[yCoordinate*toSaveForAfterGrayScale.width*4+xCoordinate*4+2] = noisedPixelsIndexArray[xCoordinate][yCoordinate];
									isPixelNoised = true;
								}
							}
						}
					}else{
						console.info('image to noise is in color');
						noisedPixels = new Uint8ClampedArray( currentInterfaceState.currentApplyImage.imageWrapper.imageData.data );
						for(var i=0;i<maxIterationIndex;i++){
							var isPixelNoised = false;
							//Using Math.floor(Math.random() * (max - min +1)) + min for obtaining EVEN distribution
							//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
							//http://javascript.info/tutorial/number-math#tofixed-precision
							var emergencyEscape = 0;
							while(!isPixelNoised&&emergencyEscape < 100){
								emergencyEscape++;
								var xCoordinate = parseInt(Math.floor(Math.random()*(currentInterfaceState.currentApplyImage.imageWrapper.width)));
								var yCoordinate = parseInt(Math.floor(Math.random()*(currentInterfaceState.currentApplyImage.imageWrapper.height)));
								if(!noisedPixelsIndexArray[xCoordinate][yCoordinate] && noisedPixelsIndexArray[xCoordinate][yCoordinate]!==0 ){
									noisedPixelsIndexArray[xCoordinate][yCoordinate] = true;
									noisedPixels[yCoordinate*currentInterfaceState.currentApplyImage.imageWrapper.width*4+xCoordinate*4] = Math.floor(Math.random()*2) ? 0 : 255;
									noisedPixels[yCoordinate*currentInterfaceState.currentApplyImage.imageWrapper.width*4+xCoordinate*4+1] = Math.floor(Math.random()*2) ? 0 : 255;
									noisedPixels[yCoordinate*currentInterfaceState.currentApplyImage.imageWrapper.width*4+xCoordinate*4+2] = Math.floor(Math.random()*2) ? 0 : 255;
									isPixelNoised = true;
								}
							}
							if(emergencyEscape===100){
								console.info('emergency escaping from noising');
							}
						}
					}
					if( currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top') ){
						currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', noisedPixels);
					}else{
						currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', noisedPixels);
						}
						currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(currentApplyMonitor.controlInterface.getImageCopy('on top'));
					}
					noiseIterationReady = true;
				};
				var filterAlreadyWorking = false;
				var filterImage = function(params){
					var imageToFilter = params.imageToFilter;
					var originalPixels = new Uint8ClampedArray( imageToFilter.imageData.data );
					var filteredPixels = new Uint8ClampedArray( imageToFilter.imageData.data );
					var filterMask = params.filterMask;
					var filterMaskDevisor = params.filterMaskDevisor;
					var treatImageAsGray = params.treatImageAsGray;
					for(var j = 0; j < imageToFilter.height; j++){
						for(var i = 0; i < imageToFilter.width; i++){
							var localConvolutionOfRedComponent = 0;
							var localConvolutionOfGreenComponent = 0;
							var localConvolutionOfBlueComponent = 0;
							for(var relativeI=-((filterMask.length-1)/2);relativeI<=((filterMask.length-1)/2);relativeI++){
								for(var relativeJ=-((filterMask.length-1)/2);relativeJ<=((filterMask.length-1)/2);relativeJ++){
									if(originalPixels[j*4*imageToFilter.width+4*i+4*relativeI+4*relativeJ*imageToFilter.width]){
										localConvolutionOfRedComponent += 
											filterMaskDevisor ? (originalPixels[j*4*imageToFilter.width+4*i+4*relativeI+4*relativeJ*imageToFilter.width]*filterMask[relativeI+((filterMask.length-1)/2)][relativeJ+((filterMask.length-1)/2)]/filterMaskDevisor) :
												(originalPixels[j*4*imageToFilter.width+4*i+4*relativeI+4*relativeJ*imageToFilter.width]*filterMask[relativeI+((filterMask.length-1)/2)][relativeJ+((filterMask.length-1)/2)]);
										if(!treatImageAsGray){
											localConvolutionOfGreenComponent += 
												filterMaskDevisor ? (originalPixels[j*4*imageToFilter.width+4*i+4*relativeI+4*relativeJ*imageToFilter.width+1]*filterMask[relativeI+((filterMask.length-1)/2)][relativeJ+((filterMask.length-1)/2)]/filterMaskDevisor) :
													(originalPixels[j*4*imageToFilter.width+4*i+4*relativeI+4*relativeJ*imageToFilter.width+1]*filterMask[relativeI+1][relativeJ+1]);
											localConvolutionOfBlueComponent += 
												filterMaskDevisor ? (originalPixels[j*4*imageToFilter.width+4*i+4*relativeI+4*relativeJ*imageToFilter.width+2]*filterMask[relativeI+((filterMask.length-1)/2)][relativeJ+((filterMask.length-1)/2)]/filterMaskDevisor) :
													(originalPixels[j*4*imageToFilter.width+4*i+4*relativeI+4*relativeJ*imageToFilter.width+2]*filterMask[relativeI+((filterMask.length-1)/2)][relativeJ+((filterMask.length-1)/2)]);
										}
									}
								}
							}
							if(!treatImageAsGray){
								filteredPixels[j*4*imageToFilter.width+4*i] = localConvolutionOfRedComponent;
								filteredPixels[j*4*imageToFilter.width+4*i+1] = localConvolutionOfGreenComponent;
								filteredPixels[j*4*imageToFilter.width+4*i+2] = localConvolutionOfBlueComponent;
							}else{
								filteredPixels[j*4*imageToFilter.width+4*i] =
								filteredPixels[j*4*imageToFilter.width+4*i+1] =
								filteredPixels[j*4*imageToFilter.width+4*i+2] =
									localConvolutionOfRedComponent;
							}
						}
						
					}
					imageToFilter.imageData.data.set( filteredPixels );
					return imageToFilter;
				}
				var imageBeforeFiltration;
				classicFilter.controlInterfaceParams.onPress = function(state){
					if(filterAlreadyWorking||skipEvents)return;
					filterAlreadyWorking = true;
					if(state === 'on'){
						if(gaussianFilter3x3.controlInterface.getState()==='on'){
							gaussianFilter3x3.controlInterface.off();
						}
						if(gaussianFilter5x5.controlInterface.getState()==='on'){
							gaussianFilter5x5.controlInterface.off();
						}
						if(noiseSlider.controlInterface.getState() === 'off'){
							classicFilter.controlInterface.off();
							gaussianFilter3x3.controlInterface.off();
							gaussianFilter5x5.controlInterface.off();
							return;
						}
						var filteredImage;
						imageBeforeFiltration = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
						if(displayedGrayScaleImage){
							filteredImage = filterImage({
								imageToFilter:currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top'),
								filterMask:[
									[1,1,1],
									[1,1,1],
									[1,1,1]
								],
								filterMaskDevisor:9,
								treatImageAsGray:true
							});
						}else{
							filteredImage = filterImage({
								imageToFilter:currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top'),
								filterMask:[
									[1,1,1],
									[1,1,1],
									[1,1,1]
								],
								filterMaskDevisor:9
							});
						}
						if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', filteredImage.imageData.data);
							}
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(filteredImage);
						}else{
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', new Uint8ClampedArray( filteredImage.imageData.data ));
						}
					}else{
						if(imageBeforeFiltration){
							if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
									currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', imageBeforeFiltration.imageData.data);
									imageBeforeFiltration = undefined;
								}
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(imageBeforeFiltration);
							}else{
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', new Uint8ClampedArray( imageBeforeFiltration.imageData.data ));
								imageBeforeFiltration = undefined;
							}
						}
					}
					filterAlreadyWorking = false;
				};
				gaussianFilter3x3.controlInterfaceParams.onPress = function(state){
					if(filterAlreadyWorking||skipEvents)return;
					filterAlreadyWorking = true;
					if(state === 'on'){
						if(classicFilter.controlInterface.getState()==='on'){
							classicFilter.controlInterface.off();
						}
						if(gaussianFilter5x5.controlInterface.getState()==='on'){
							gaussianFilter5x5.controlInterface.off();
						}
						if(noiseSlider.controlInterface.getState() === 'off'){
							classicFilter.controlInterface.off();
							gaussianFilter3x3.controlInterface.off();
							gaussianFilter5x5.controlInterface.off();
							return;
						}
						var filteredImage;
						imageBeforeFiltration = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
						if(displayedGrayScaleImage){
							filteredImage = filterImage({
								imageToFilter:currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top'),
								filterMask:[
									[1,2,1],
									[2,4,2],
									[1,2,1]
								],
								filterMaskDevisor:16,
								treatImageAsGray:true
							});
						}else{
							filteredImage = filterImage({
								imageToFilter:currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top'),
								filterMask:[
									[1,2,1],
									[2,4,2],
									[1,2,1]
								],
								filterMaskDevisor:16
							});
						}
						if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', filteredImage.imageData.data);
							}
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(filteredImage);
						}else{
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', new Uint8ClampedArray( filteredImage.imageData.data ));
						}
					}else{
						if(imageBeforeFiltration){
							if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
									currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', imageBeforeFiltration.imageData.data);
									imageBeforeFiltration = undefined;
								}
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(imageBeforeFiltration);
							}else{
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', new Uint8ClampedArray( imageBeforeFiltration.imageData.data ));
								imageBeforeFiltration = undefined;
							}
						}
					}
					filterAlreadyWorking = false;
				};
				gaussianFilter5x5.controlInterfaceParams.onPress = function(state){
					if(filterAlreadyWorking||skipEvents)return;
								filterAlreadyWorking = true;
					if(state === 'on'){
						if(gaussianFilter3x3.controlInterface.getState()==='on'){
							gaussianFilter3x3.controlInterface.off();
						}
						if(classicFilter.controlInterface.getState()==='on'){
							classicFilter.controlInterface.off();
						}
						if(noiseSlider.controlInterface.getState() === 'off'){
							classicFilter.controlInterface.off();
							gaussianFilter3x3.controlInterface.off();
							gaussianFilter5x5.controlInterface.off();
							return;
						}
						var filteredImage;
						imageBeforeFiltration = currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top');
						if(displayedGrayScaleImage){
							filteredImage = filterImage({
								imageToFilter:currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top'),
								filterMask:[
									[1,4,6,4,1],
									[4,16,24,16,4],
									[6,24,36,24,6],
									[4,16,24,16,4],
									[1,4,6,4,1]
								],
								filterMaskDevisor:256,
								treatImageAsGray:true
							});
						}else{
							filteredImage = filterImage({
								imageToFilter:currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImageCopy('on top'),
								filterMask:[
									[1,4,6,4,1],
									[4,16,24,16,4],
									[6,24,36,24,6],
									[4,16,24,16,4],
									[1,4,6,4,1]
								],
								filterMaskDevisor:256
							});
						}
						if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', filteredImage.imageData.data);
							}
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(filteredImage);
						}else{
							currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', new Uint8ClampedArray( filteredImage.imageData.data ));
						}
					}else{
						if(imageBeforeFiltration){
							if(!currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.getImage('on top')){
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterfaceParams.onLoad = function(){
									currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', imageBeforeFiltration.imageData.data);
									imageBeforeFiltration = undefined;
								}
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.addImageFromWrapper(imageBeforeFiltration);
							}else{
								currentInterfaceState.currentDisplayMonitor.monitor.controlInterface.applyToImage('on top', new Uint8ClampedArray( imageBeforeFiltration.imageData.data ));
								imageBeforeFiltration = undefined;
							}
						}
					}
					filterAlreadyWorking = false;
				};
				var imageBeforeDifferenceDetection;
				calculateDifferenceButton.controlInterfaceParams.onPress = function(state){
					if(state === "on"){
						var monitor2Image = firstResultMonitor.controlInterface.getImageCopy('on top');
						var monitor3Image = secondResultMonitor.controlInterface.getImageCopy('on top');
						imageBeforeDifferenceDetection = secondResultMonitor.controlInterface.getImageCopy('on top');
						if(!monitor2Image || !monitor3Image){
							calculateDifferenceButton.controlInterface.off();
							return;
						}
						var pixels2 = new Uint8ClampedArray(monitor2Image.imageData.data);
						var pixels3 = new Uint8ClampedArray(monitor3Image.imageData.data);
						var resultPixels = new Uint8ClampedArray(monitor3Image.imageData.data);
						for(var i=0; i<monitor3Image.width; i++){
							for(var j=0; j<monitor3Image.height; j++){
								if( pixels3[j*4*monitor3Image.width+4*i]!==pixels2[j*4*monitor3Image.width+4*i]||
									pixels3[j*4*monitor3Image.width+4*i+1]!==pixels2[j*4*monitor3Image.width+4*i+1]||
									pixels3[j*4*monitor3Image.width+4*i+2]!==pixels2[j*4*monitor3Image.width+4*i+2]
								){
									resultPixels[j*4*monitor3Image.width+4*i] = resultPixels[j*4*monitor3Image.width+4*i+1] = resultPixels[j*4*monitor3Image.width+4*i+2] = 255;
								}else{
									resultPixels[j*4*monitor3Image.width+4*i] = resultPixels[j*4*monitor3Image.width+4*i+1] = resultPixels[j*4*monitor3Image.width+4*i+2] = 0;
								}
							}
						}
						secondResultMonitor.controlInterface.applyToImage('on top', resultPixels);
					}else if(state==="off"){
						if(imageBeforeDifferenceDetection){
							secondResultMonitor.controlInterface.applyToImage('on top', imageBeforeDifferenceDetection.imageData.data);
							imageBeforeDifferenceDetection = undefined;
						}
					}
				};
			}
		</script>
		<title>Dimitar Nikolov's personal page</title>
	</head>
	<body onload="init();">
		<header id="header" class="section">
				<div id="menuWrapper">
					<nav>
						<ul id="mainMenu">
							<li class="menuItem">
								<a href="/index.html">
									<div  id="home" class="button">
										<img src="/images/reflection/Home.png" />
										<span>Home</span>
									</div>
								</a>
							</li>
							<li class="menuItem">
								<a href="/html/aboutme.html">
									<div id="aboutme" class="button">
										<img src="/images/reflection/User.png" />
										<span>About me</span>
									</div>
								</a>
							</li>
							<li class="menuItem">							
								<a href="/html/weblog.html">
									<div id="compositions" class="button">
										<img src="/images/reflection/Quill.png" />
										<span>Weblog</span>
									</div>
								</a>
							</li>			
							<li class="menuItem">
								<a href="/html/projects.html">
									<div id="projects" class="button">
										<img src="/images/reflection/Oscilloscope.png" />
										<span>Projects</span>
									</div>
								</a>
							</li>				
							<li  class="menuItem">
								<a href="/html/services.html">
									<div id="services" class="button">
										<img src="/images/reflection/Computer.png" />
										<span>Services</span>
									</div>
								</a>
							</li>
							<li class="menuItem">
								<a href="/html/connect.html">
									<div id="connect" class="button">
										<img src="/images/Connect.png" />
										<span>Connect</span>
									</div>
								</a>
							</li>	
							<span class="line">
								&nbsp;
							</span>
						</ul>
					</nav>
				</div>
		</header>
		<main id="main" class="section main">
			<section id="underConstruction" >
				<img  src="/images/UnderConstructionTools.png" class="background"/>
				<img  src="/images/UnderConstructionToolsSchemed.png" />
				<h1><span class="emphase">This site is still under construction.</span></h1>
				<p>
					This site or some parts of it could be redesigned, features could be added or removed.
				</p>
				<p>
					If you are experiencing any problems with the site or you are noticing irregular behaviour, please do
				not hesitate to , send me an email at <a id="sendEmail" target="_new" href="mailto:dnikolov.co.nf@gmail.com" >dnikolov.co.nf@gmail.com</a>
				or contact me using <a id="mailform" href="mailform.html" target="_new" >this</a> form.
				</p>
			</section>
		</main>
		<footer id="footer" class="section"></footer>
	</body>
</html>
